package com.martin.lib_base.utils

import com.martin.lib_base.BuildConfig
import com.martin.lib_base.bean.SocketEvent
import com.martin.lib_base.bean.SocketEventBean
import com.martin.lib_base.constant.ConstantBus
import com.martin.lib_base.constant.ConstantKey
import com.martin.lib_base.ktx.loge
import com.martin.lib_base.ktx.showToast
import com.martin.lib_base.net.Api
import com.martin.lib_base.net.NetApply
import com.martin.lib_base.net.log.HttpLogImpl
import com.tencent.mmkv.MMKV
import dev.utils.app.HandlerUtils
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.WebSocket
import okhttp3.WebSocketListener
import okhttp3.logging.HttpLoggingInterceptor
import java.util.concurrent.TimeUnit

/**
 * @author：孟凡华
 * @date：2023/4/18
 * @desc：websocket工具类
 */
object WebSocketUtil {

    private const val TAG = "WebSocketUtil"

    // 连接超时时间(S)
    private const val TIMEOUT = 360L

    // OkHttpClient对象
    private val okHttpClient: OkHttpClient

    // WebSocket对象
    private var webSocket: WebSocket? = null

    // WebSocket是否连接
    private var connectState = false

    // 重连次数
    private var retryCount = 0

    // 发送失败测试
    private var sendErrorCount = 0

    private val retryLimit = 99

    // WebSocket监听
    private val webSocketListener = object : WebSocketListener() {
        override fun onOpen(webSocket: WebSocket, response: okhttp3.Response) {
            showLog("onOpen")
            retryCount = 0
            sendErrorCount = 0
            connectState = true
            sendHeartBeat()
        }

        override fun onMessage(webSocket: WebSocket, text: String) {
            // 心跳事件及空数据不处理
            if (text == "pong" || text == "ping" || text.isEmpty()) return

            showLog("onMessage: $text")

            // 非对象类型不处理
            if (!text.startsWith("{")) return

            // 解析消息
            val eventBean = NetApply.gson.fromJson(text, SocketEventBean::class.java)
            // 事件总线发送消息
            FlowBus.with<SocketEventBean>(ConstantBus.SOCKET_EVENT)
                .tryEmit(eventBean)

            when (eventBean.type) {
                SocketEvent.NOTICE.value, SocketEvent.PAID.value -> {
                    showToast(eventBean.msg, false)
                }

                else -> {}
            }
        }

        override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
            showLog("onClosed: $code, $reason")
            closeWebSocket("onClosed")
        }

        override fun onFailure(webSocket: WebSocket, t: Throwable, response: okhttp3.Response?) {
            showLog("onFailure: ${t.message}")
            if (retryCount < retryLimit) {
                HandlerUtils.postRunnable({
                    connect()
                    retryCount++
                }, 2000)
            } else {
                closeWebSocket("重连次数: $retryCount 次")
            }
        }
    }

    // 心跳Runnable
    private val heartBeatRunnable = Runnable {
        sendMsg("ping")
        sendHeartBeat()
    }

    init {
        val logInterceptor = HttpLoggingInterceptor(HttpLogImpl())
        logInterceptor.level = HttpLoggingInterceptor.Level.BODY

        okHttpClient = OkHttpClient.Builder()
            .readTimeout(TIMEOUT, TimeUnit.SECONDS)
            .writeTimeout(TIMEOUT, TimeUnit.SECONDS)
            .connectTimeout(TIMEOUT, TimeUnit.SECONDS)
            .retryOnConnectionFailure(true)
            .addInterceptor(logInterceptor)
            //.addInterceptor(SignAndEncryptionInterceptor())
            .build()
    }

    /**
     * 创建WebSocket对象
     */
    private fun createWebSocket() {
        // ws://后端ip:端口/resource/websocket?clientid=import.meta.env.VITE_APP_CLIENT_ID&Authorization=Bearer eyJ0eXAiO......
        val url = Api.BASE_URL
            .replace("http://", "ws://")
            .replace(
                "https://",
                "ws://"
            ) + "resource/websocket?clientid=${BuildConfig.clientId}&Authorization=Bearer ${
            MMKV.defaultMMKV().decodeString(ConstantKey.TOKEN)
        }"
        showLog("WebSocket地址：$url")

        val request =
            Request.Builder().url(url).build()
        webSocket = okHttpClient.newWebSocket(request, webSocketListener)
    }

    /**
     * 连接WebSocket
     */
    fun connect() {
        if (!connectState) {
            createWebSocket()
        }
    }

    /**
     * 发送心跳
     */
    private fun sendHeartBeat() {
        HandlerUtils.postRunnable(heartBeatRunnable, 5000)
    }

    /**
     * 停止心跳
     */
    fun stopHeartBeat() {
        HandlerUtils.removeRunnable(heartBeatRunnable)
    }

    /**
     * 发送消息
     */
    fun sendMsg(msg: String) {
        val isSend = webSocket?.send(msg)
        if (isSend == true && sendErrorCount != 0) {
            sendErrorCount = 0
        } else {
            sendErrorCount++
        }
        if (isSend == true && msg == "ping") return
        showLog("发送消息：$msg, 是否发送成功：$isSend, 发送失败次数：$sendErrorCount")

        if (sendErrorCount > 5) {
            closeWebSocket("发送消息失败，次数：$sendErrorCount")
            connect()
        }
    }

    /**
     * 关闭WebSocket
     */
    fun closeWebSocket(msg: String = "主动关闭") {
        stopHeartBeat()
        webSocket?.close(1000, msg)
        webSocket = null
        connectState = false
    }

    private fun showLog(msg: String) {
        loge("$TAG : $msg")
    }
}
